/*

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     licenses@blazegraph.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/
/*
 * Created on Dec 26, 2008
 */

package com.bigdata.service;

import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ExecutionException;

import com.bigdata.btree.IIndex;
import com.bigdata.btree.IndexMetadata;
import com.bigdata.btree.proc.IIndexProcedure;
import com.bigdata.journal.ITx;

/**
 * Unit tests of local (all writes are on a single data service) and distributed
 * abort and commit protocols for an {@link IBigdataFederation} using the
 * {@link DistributedTransactionService}.
 * 
 * @todo the easiest way to set this up is to place different indices onto
 *       different data services, using just 2 data services to make setup
 *       easier.
 *       <p>
 *       An alternative setup would pre-partition a single scale-out index so
 *       that it was on both data services.
 * 
 * @todo There should be explicit tests of distributed transactions that involve
 *       different indices, including the case where the MDS is participating in
 *       the tx. This should really be no different, but the tests should cover
 *       the case anyway.  In point, perhaps the easiest thing to do is to register
 *       different indices on each data service since the commit protocol is
 *       entirely in terms of the local index resources (it pays no attention
 *       to scale-out indices, so this is best really).
 * 
 * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
 * @version $Id$
 */
public class TestDistributedTransactionService extends
        AbstractEmbeddedFederationTestCase {

    /**
     * 
     */
    public TestDistributedTransactionService() {
    }

    /**
     * @param arg0
     */
    public TestDistributedTransactionService(String arg0) {
        super(arg0);
    }

//    /**
//     * Writes a key/val pair on a named index on the specified
//     * {@link IDataService}.
//     * 
//     * @param tx
//     *            The timestamp or transaction identifer.
//     * @param ds
//     *            The {@link IDataService}.
//     * @param name
//     *            The index name.
//     *            
//     * @throws InterruptedException
//     * @throws ExecutionException
//     * @throws IOException
//     */
//    protected void doWrite(long tx, IDataService ds, String name)
//            throws InterruptedException, ExecutionException, IOException {
//
//        ds.submit(tx, name, new IIndexProcedure() {
//
//            public Object apply(IIndex ndx) {
//                
//                // write on the index.
//                ndx.insert(new byte[]{1}, new byte[]{1});
//                
//                return null;
//            }
//
//            /** read-write. */
//            public boolean isReadOnly() {
//                return false;
//            }});
//    
//    }
    
    /**
     * Unit test of abort of a read-write tx that writes on a single data
     * service.
     * 
     * @throws ExecutionException
     * @throws InterruptedException
     * @throws IOException
     */
    public void test_localTxAbort() throws IOException, InterruptedException, ExecutionException {
        
        final String name1 = "ndx1";
        
        {
            final IndexMetadata md = new IndexMetadata(name1, UUID
                    .randomUUID());
            
            md.setIsolatable(true);
            
            dataService1.registerIndex(name1, md);
        }
        
        final long tx = fed.getTransactionService().newTx(ITx.UNISOLATED);
        
        // submit write operation to the ds.
        dataService1.submit(tx, name1, new IIndexProcedure<Void>(){

			private static final long serialVersionUID = 1L;

			@Override
            public Void apply(final IIndex ndx) {
                
                // write on the index.
                ndx.insert(new byte[]{1}, new byte[]{1});
                
                return null;
            }

            public boolean isReadOnly() {
                return false;// read-write.
            }}).get();
        
        // verify write not visible to unisolated operation.
        dataService1.submit(ITx.UNISOLATED, name1, new IIndexProcedure<Void>(){

			private static final long serialVersionUID = 1L;

			@Override
            public Void apply(final IIndex ndx) {

                // verify not in the index.
                assertFalse(ndx.contains(new byte[]{1}));
                
                return null;
            }

            public boolean isReadOnly() {
                return false;// read-write.
            }}).get();

        // abort the tx.
        fed.getTransactionService().abort(tx);

        // verify write still not visible.
        dataService1.submit(ITx.UNISOLATED, name1, new IIndexProcedure<Void>(){

			private static final long serialVersionUID = 1L;

			@Override
            public Void apply(final IIndex ndx) {

                // verify not in the index.
                assertFalse(ndx.contains(new byte[]{1}));
                
                return null;
            }

            public boolean isReadOnly() {
                return false;// read-write.
            }}).get();

        // verify operation rejected for aborted read-write tx.
        try {

			dataService1.submit(tx, name1, new IIndexProcedure<Void>() {

				private static final long serialVersionUID = 1L;

				@Override
	            public Void apply(final IIndex ndx) {

	        		// NOP
					return null;
				}

				public boolean isReadOnly() {
					return false;// read-write.
				}
			}).get();

			fail("Expecting exception");
			
		} catch (Throwable t) {
			
			log.info("Ignoring expected error: " + t);
			
		}

    }

// FIXME full distributed read-write tx support is not finished yet so these
// tests have been commented out.
//    /**
//     * unit test of commit of a read-write tx that writes on a single data
//     *       service.
//     * 
//     * @throws IOException 
//     * @throws ExecutionException 
//     * @throws InterruptedException 
//     */
//    public void test_localTxCommit() throws InterruptedException,
//            ExecutionException, IOException {
//        
//        final String name1 = "ndx1";
//        
//        {
//            final IndexMetadata md = new IndexMetadata(name1, UUID
//                    .randomUUID());
//            
//            md.setIsolatable(true);
//            
//            dataService1.registerIndex(name1, md);
//        }
//        
//        final long tx = fed.getTransactionService().newTx(ITx.UNISOLATED);
//        
//        // submit write operation to the ds.
//        dataService1.submit(tx, name1, new IIndexProcedure(){
//
//            public Object apply(IIndex ndx) {
//                
//                // write on the index.
//                ndx.insert(new byte[]{1}, new byte[]{1});
//                
//                return null;
//            }
//
//            public boolean isReadOnly() {
//                return false;// read-write.
//            }}).get();
//        
//        // verify write not visible to unisolated operation.
//        dataService1.submit(ITx.UNISOLATED, name1, new IIndexProcedure(){
//
//            public Object apply(IIndex ndx) {
//                
//                // verify not in the index.
//                assertFalse(ndx.contains(new byte[]{1}));
//                
//                return null;
//            }
//
//            public boolean isReadOnly() {
//                return false;// read-write.
//            }}).get();
//
//        // commit the tx.
//        final long commitTime = fed.getTransactionService().commit(tx);
//
//        // verify write now visible as of that commit time.
//        dataService1.submit(commitTime, name1, new IIndexProcedure(){
//
//            public Object apply(IIndex ndx) {
//                
//                // verify in the index.
//                assertTrue(ndx.contains(new byte[]{1}));
//                
//                return null;
//            }
//
//            public boolean isReadOnly() {
//                return true;// read-only.
//            }}).get();
//
//        // verify operation rejected for committed read-write tx.
//        try {
//        dataService1.submit(tx, name1, new IIndexProcedure(){
//
//            public Object apply(IIndex ndx) {
//                // NOP
//                return null;
//            }
//
//            public boolean isReadOnly() {
//                return false;// read-write.
//            }}).get();
//        fail("Expecting exception");
//        } catch(Throwable t) {
//            log.info("Ignoring expected error: "+t);
//        }
//        
//    }
//
//    /**
//     * @todo unit test of abort of a read-write tx that writes on a more than
//     *       one data service.
//     */
//    public void test_distTxAbort() {
//        
//        fail("write test");
//        
//    }
//    
//    /**
//     * @todo unit test of commit of a read-write tx that writes on a more than
//     *       one data service.
//     */
//    public void test_distTxCommit() {
//        
//        fail("write test");
//        
//    }

}
